In this article, I share some common JavaScript interview questions for frontend engineers.
The best way to use this article is to try answering these questions in English.
==
, ===
, and Object.is()
in JavaScript?在 JavaScript 當中,==
、===
與 Object.is()
的區別是?
Strict Equality (===)
:
of different types 是固定用法,表示「屬於不同的類型」。
Loose Equality (==)
:
Object.is()
:
在 JavaScript 中 0.1 + 0.2 為什麼不是 0.3?如何避免相關問題?
JavaScript uses the IEEE 754 double-precision format to represent numbers. When performing calculations, the computer converts decimal numbers to binary. However, some decimal fractions, such as 0.1 and 0.2, cannot be represented precisely in binary because the conversion results in repeating fractions. This is why 0.1 + 0.2
does not equal exactly 0.3
.
How can we avoid this problem? One common approach is to convert floating-point numbers to integers, perform the arithmetic, and then convert them back to floating-point numbers at the end. Another approach is to use specialized libraries designed for precise decimal arithmetic, such as decimal.js, big.js, or math.js.
const resultAsInt = (0.1 * 10) + (0.2 * 10); // 1 + 2 = 3
const finalResult = resultAsInt / 10; // 0.3
在 JavaScript 一律使用浮點數來表示數值,其遵循 IEEE754 規範,使用 64 位固定長度表示(包含符號位 1 個、指數位 11 個、尾數位 52 個),然後電腦在計算數值時會先轉成二進制進行計算,但 0.1 & 0.2 轉換成二進制表示時,計算範例如下,取整後會一直有餘數,最終超過 52 位,之後後面的會被捨棄,導致精度丟失,所以會發生預計計算結果和實際結果不符的情況。
十進制轉二進制是除二取整
5 / 2 = 2 餘 1
2 / 2 = 1 餘 0
1 / 2 = 0 餘 1
碰到小數則是乘二取整,0.1 & 0.2 取整後會一直有餘數
0.1 * 2 = 0.2 取整 0 剩下 0.2
0.2 * 2 = 0.4 取整 0 剩下 0.4
0.4 * 2 = 0.8 取整 0 剩下 0.8
0.8 * 2 = 1.6 取整 1 剩下 0.6
0.6 * 2 = 1.2 取整 1 剩下 0.2
0.2 * 2 = 0.4 取整 0 剩下 0.4
0.4 * 2 = 0.8 取整 0 剩下 0.8
0.8 * 2 = 1.6 取整 1 剩下 0.6
0.6 * 2 = 1.2 取整 1 剩下 0.2
(0.1).toString(2) // '0.0001100110011001100110011001100110011001100110011001101'
(0.2).toString(2) // '0.001100110011001100110011001100110011001100110011001101'
var
, let
, and const
in JavaScript?在 JavaScript 中用 var
, let
, 以及 const
有什麼差別?
var
:
var
are hoisted to the top of their function or global scope. You can use the variable before it's declared in the code, though its value will be undefined
.var
variable within the same scope without any errors.let
:
let
is only accessible within the curly braces ({}
) where it was defined.let
variables are also hoisted, but they are not initialized. This results in a "Temporal Dead Zone". Trying to access a let
variable before its declaration will result in a ReferenceError
.let
variable, but you can't redeclare it within the same scope.const
:
const
must be initialized at the time of declaration. If you don't initial a value when declaring a const
variable, it will result in a SyntaxError
.什麼是閉包 (Closure)?
When you define a function inside another function, the inner function has access to the variables and parameters of its outer function. A closure is formed when this inner function is returned or passed to another place in your code. It keeps a live reference to the outer function's environment, allowing it to continue accessing and even modifying those variables long after the outer function has finished executing.
Key Characteristics:
回答中用到了 long after + [時間點/事件/名詞子句],指的是在某個時間或事件過去很久之後。
These tools will survive long after the event concludes.
這些工具在活動結束之後很久仍然會存在。
this
is determined in different contexts.解釋不同情境下 this
的指向?
this
is a keyword whose value is determined by the context in which a function is called.
In the global execution context and non–strict mode, this
refers to the global object. In a browser environment, this is the window object. In Node.js, it is the global object.
In the global execution context and strict mode, this
is undefined
.
The value of this
inside a function depends on how the function is called.
Simple Function Call: In non-strict mode, this
refers to the global object.
function showThis() {
console.log(this);
}
showThis(); // In non-strict mode: window (or global). In strict mode: undefined.
Method Call: When a function is called as a method of an object, this
refers to the object that the method belongs to.
const person = {
name: 'John',
greet: function() {
console.log(`Hello, my name is ${this.name}`);
}
};
person.greet(); // 'Hello, my name is John'.
When a function is used as a constructor with the new
keyword, this
refers to the newly created object instance.
function Person(name) {
this.name = name; // 'this' is the new object instance.
}
const john = new Person('John');
console.log(john.name); // 'John'
Arrow functions (() => {}
) have a unique behavior: they do not have their own this
. Instead, they inherit this
from the surrounding lexical scope (the parent function or global scope). This makes them very predictable and a good choice for callbacks.
const person = {
name: 'John',
// Traditional function: 'this' refers to the object itself
showName: function() {
setTimeout(function() {
console.log(this.name); // 'this' is 'window' (or global) here, so it's undefined.
}, 100);
},
// Arrow function: 'this' is inherited from the 'person' object
showNameArrow: function() {
setTimeout(() => {
console.log(this.name); // 'this' is 'person'.
}, 100);
}
};
person.showName(); // undefined
person.showNameArrow(); // 'John'
You can explicitly set the value of this
using the call()
, apply()
, and bind()
methods.
call()
and apply()
: Call the function immediately with a specified this
value. The only difference is how they handle arguments (call
takes them individually, apply
takes an array).
bind()
: Returns a new function with a permanently bound this
value, without immediately executing it.
function showThisName(age) {
console.log(`${this.name} is ${age} years old.`);
}
const person1 = { name: 'John' };
const person2 = { name: 'Jane' };
showThisName.call(person1, 30); // 'John is 30 years old.'
showThisName.apply(person2, [25]); // 'Jane is 25 years old.'
const boundFunction = showThisName.bind(person1);
boundFunction(40); // 'John is 40 years old.'